/**
 * Markdown转PPT核心转换器
 * 将Markdown内容解析并转换为PowerPoint演示文稿
 */

import PptxGenJs from 'pptxgenjs';
import MarkdownIt from 'markdown-it';
import { promises as fs } from 'fs';
import { join, dirname, basename } from 'path';
import { tmpdir } from 'os';
import { nanoid } from 'nanoid';
import fetch from 'node-fetch';
import type { 
  MarkdownToPptOptions, 
  PptConversionResult,
  MarkdownToPptParams,
  MarkdownFileToPptParams 
} from './types.js';
import { createChildLogger, createErrorLogContext } from '../../../utils/logger.js';

export class MarkdownToPptConverter {
  private logger = createChildLogger('MarkdownToPptConverter');
  private md: MarkdownIt;

  constructor() {
    this.md = new MarkdownIt({
      html: true,
      linkify: true,
      typographer: true
    });
  }

  /**
   * 转换Markdown内容为PPT
   */
  async convertToPpt(params: MarkdownToPptParams): Promise<PptConversionResult> {
    const { markdown, outputPath, options = {} } = params;

    try {
      this.logger.info('Starting Markdown to PPT conversion', {
        contentLength: markdown.length,
        outputPath,
        options
      });

      // 确保输出目录存在
      await fs.mkdir(dirname(outputPath), { recursive: true });

      // 解析Markdown
      const parsedContent = this.parseMarkdown(markdown, options);

      // 创建PPT
      const ppt = this.createPpt(options);

      // 添加幻灯片
      await this.addSlidesToPpt(ppt, parsedContent, options);

      // 保存PPT
      await this.savePpt(ppt, outputPath);

      // 获取文件大小
      const stats = await fs.stat(outputPath);

      const result: PptConversionResult = {
        success: true,
        outputPath,
        size: stats.size,
        slideCount: parsedContent.slides.length,
        message: `成功生成PPT，共${parsedContent.slides.length}页幻灯片`
      };

      this.logger.info('Markdown to PPT conversion completed', result);
      return result;

    } catch (error) {
      this.logger.error(
        'Markdown to PPT conversion failed',
        createErrorLogContext(error, { outputPath })
      );
      throw error;
    }
  }

  /**
   * 转换Markdown文件为PPT
   */
  async convertFileToPpt(params: MarkdownFileToPptParams): Promise<PptConversionResult> {
    try {
      this.logger.info('Reading Markdown file', { filePath: params.filePath });
      
      const markdown = await fs.readFile(params.filePath, 'utf-8');
      
      return await this.convertToPpt({
        markdown,
        outputPath: params.outputPath,
        options: params.options
      });
    } catch (error) {
      this.logger.error(
        'Failed to read Markdown file',
        createErrorLogContext(error, { filePath: params.filePath })
      );
      throw error;
    }
  }

  /**
   * 解析Markdown内容
   */
  private parseMarkdown(markdown: string, options: MarkdownToPptOptions) {
    const tokens = this.md.parse(markdown, {});
    const slides: ParsedSlide[] = [];
    let currentSlide: ParsedSlide | null = null;

    for (let i = 0; i < tokens.length; i++) {
      const token = tokens[i];
      if (!token) continue;

      switch (token.type) {
        case 'heading_open':
          const level = parseInt(token.tag.replace('h', ''));
          const nextToken = tokens[i + 1];
          const title = nextToken?.content || '';

          // 根据配置决定是否创建新幻灯片
          const shouldCreateNewSlide = 
            (level === 1 && options.h1AsNewSlide !== false) ||
            (level === 2 && options.h2AsNewSlide === true);

          if (shouldCreateNewSlide || currentSlide === null) {
            // 保存之前的幻灯片
            if (currentSlide) {
              slides.push(currentSlide);
            }

            // 创建新幻灯片
            currentSlide = {
              title,
              level,
              content: [],
              images: [],
              lists: []
            };
          } else {
            // 添加到当前幻灯片的内容中
            if (currentSlide) {
              currentSlide.content.push({ type: 'heading', level, text: title });
            }
          }
          break;

        case 'paragraph_open':
          // 处理段落内容
          const paragraphContent = this.extractParagraphContent(tokens, i);
          if (paragraphContent.trim() && currentSlide) {
            currentSlide.content.push({ type: 'paragraph', text: paragraphContent });
          }
          break;

        case 'bullet_list_open':
        case 'ordered_list_open':
          // 处理列表
          const listItems = this.extractListItems(tokens, i);
          if (listItems.length > 0 && currentSlide) {
            currentSlide.lists.push({
              type: token.type === 'bullet_list_open' ? 'bullet' : 'ordered',
              items: listItems
            });
          }
          break;

        case 'image':
          // 处理图片
          if (currentSlide) {
            currentSlide.images.push({
              src: token.attrGet('src') || '',
              alt: token.content || '',
              title: token.attrGet('title') || ''
            });
          }
          break;
      }
    }

    // 添加最后一个幻灯片
    if (currentSlide) {
      slides.push(currentSlide);
    }

    // 如果没有幻灯片，创建一个默认的
    if (slides.length === 0) {
      slides.push({
        title: options.title || '无标题演示',
        level: 1,
        content: [{ type: 'paragraph', text: '内容为空' }],
        images: [],
        lists: []
      });
    }

    return { slides };
  }

  /**
   * 提取段落内容
   */
  private extractParagraphContent(tokens: any[], startIndex: number): string {
    let content = '';
    for (let i = startIndex + 1; i < tokens.length; i++) {
      const token = tokens[i];
      if (token.type === 'paragraph_close') {
        break;
      }
      if (token.type === 'inline') {
        content += token.content;
      }
    }
    return content;
  }

  /**
   * 提取列表项
   */
  private extractListItems(tokens: any[], startIndex: number): string[] {
    const items: string[] = [];
    let depth = 0;

    for (let i = startIndex; i < tokens.length; i++) {
      const token = tokens[i];
      
      if (token.type.includes('list_open')) {
        depth++;
      } else if (token.type.includes('list_close')) {
        depth--;
        if (depth === 0) {
          break;
        }
      } else if (token.type === 'list_item_open' && depth === 1) {
        // 提取列表项内容
        const itemContent = this.extractListItemContent(tokens, i);
        if (itemContent.trim()) {
          items.push(itemContent);
        }
      }
    }

    return items;
  }

  /**
   * 提取列表项内容
   */
  private extractListItemContent(tokens: any[], startIndex: number): string {
    let content = '';
    let depth = 0;

    for (let i = startIndex; i < tokens.length; i++) {
      const token = tokens[i];
      
      if (token.type === 'list_item_open') {
        depth++;
      } else if (token.type === 'list_item_close') {
        depth--;
        if (depth === 0) {
          break;
        }
      } else if (token.type === 'inline' && depth === 1) {
        content += token.content;
      }
    }

    return content;
  }

  /**
   * 创建PPT实例
   */
  private createPpt(options: MarkdownToPptOptions): PptxGenJs {
    const ppt = new PptxGenJs();

    // 设置PPT属性
    ppt.author = options.author || 'Test Case Writer';
    ppt.title = options.title || 'Markdown转PPT演示';
    ppt.subject = 'Generated from Markdown';

    // 设置布局
    if (options.size === 'widescreen') {
      ppt.layout = 'LAYOUT_WIDE';
    }

    // 设置主题
    if (options.theme) {
      this.applyTheme(ppt, options.theme);
    }

    return ppt;
  }

  /**
   * 应用主题
   */
  private applyTheme(ppt: PptxGenJs, theme: string): void {
    // 这里可以根据不同主题设置不同的样式
    const themes = {
      business: {
        backgroundColor: '#1E3A5F',
        titleColor: '#FFFFFF',
        textColor: '#E8F4FD'
      },
      modern: {
        backgroundColor: '#2C3E50',
        titleColor: '#3498DB',
        textColor: '#ECF0F1'
      },
      minimal: {
        backgroundColor: '#FAFAFA',
        titleColor: '#2C3E50',
        textColor: '#34495E'
      },
      default: {
        backgroundColor: '#FFFFFF',
        titleColor: '#2E75B6',
        textColor: '#333333'
      }
    };

    // 应用主题样式（pptxgenjs的具体实现方式）
    const themeStyle = themes[theme as keyof typeof themes] || themes.default;
    // 注意：实际的主题应用需要根据pptxgenjs的API来实现
  }

  /**
   * 添加幻灯片到PPT
   */
  private async addSlidesToPpt(
    ppt: PptxGenJs, 
    parsedContent: { slides: ParsedSlide[] }, 
    options: MarkdownToPptOptions
  ): Promise<void> {
    for (const slideData of parsedContent.slides) {
      const slide = ppt.addSlide();

      // 添加标题
      slide.addText(slideData.title, {
        x: 0.5,
        y: 0.5,
        w: 9,
        h: 1,
        fontSize: options.fontSize?.title || 24,
        bold: true,
        color: '2E75B6'
      });

      let yPos = 1.8;

      // 添加内容
      for (const content of slideData.content) {
        if (content.type === 'paragraph') {
          slide.addText(content.text, {
            x: 0.5,
            y: yPos,
            w: 9,
            h: 0.8,
            fontSize: options.fontSize?.content || 14,
            color: '333333'
          });
          yPos += 0.9;
        } else if (content.type === 'heading') {
          slide.addText(content.text, {
            x: 0.5,
            y: yPos,
            w: 9,
            h: 0.6,
            fontSize: (options.fontSize?.content || 14) + 2,
            bold: true,
            color: '2E75B6'
          });
          yPos += 0.7;
        }
      }

      // 添加列表
      for (const list of slideData.lists) {
        const bulletPoints = list.items.map((item, index) => ({
          text: item,
          options: { 
            bullet: list.type === 'ordered' ? { type: 'number' as const } : true,
            indentLevel: 0 
          }
        }));

        slide.addText(bulletPoints, {
          x: 0.5,
          y: yPos,
          w: 9,
          h: Math.min(2, list.items.length * 0.3),
          fontSize: options.fontSize?.content || 14,
          color: '333333'
        });
        yPos += Math.min(2.2, list.items.length * 0.3 + 0.2);
      }

      // 添加图片
      for (const image of slideData.images) {
        try {
          const imagePath = await this.downloadImage(image.src, options.imageDownloadTimeout);
          slide.addImage({
            path: imagePath,
            x: 1,
            y: yPos,
            w: 8,
            h: 4
          });
          yPos += 4.2;
        } catch (error) {
          this.logger.warn('Failed to add image to slide', { 
            src: image.src, 
            error: error instanceof Error ? error.message : String(error) 
          });
          // 添加图片占位符文本
          slide.addText(`[图片加载失败: ${image.alt || image.src}]`, {
            x: 1,
            y: yPos,
            w: 8,
            h: 0.5,
            fontSize: 12,
            color: '999999',
            italic: true
          });
          yPos += 0.7;
        }
      }

      // 添加页码（如果启用）
      if (options.includePageNumbers) {
        slide.addText(`${parsedContent.slides.indexOf(slideData) + 1}`, {
          x: 9.2,
          y: 7,
          w: 0.5,
          h: 0.3,
          fontSize: 10,
          color: '666666'
        });
      }
    }
  }

  /**
   * 下载图片到临时目录
   */
  private async downloadImage(url: string, timeout = 10000): Promise<string> {
    if (!url.startsWith('http')) {
      // 如果是相对路径，直接返回
      return url;
    }

    try {
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), timeout);
      
      const response = await fetch(url, { 
        signal: controller.signal,
        headers: {
          'User-Agent': 'Mozilla/5.0 (compatible; Test-Case-Writer/1.0)'
        }
      });
      
      clearTimeout(timeoutId);

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const buffer = await response.buffer();
      const ext = this.getImageExtension(url, response.headers.get('content-type'));
      const tempPath = join(tmpdir(), `ppt-image-${nanoid()}.${ext}`);
      
      await fs.writeFile(tempPath, buffer);
      
      this.logger.debug('Image downloaded successfully', { url, tempPath });
      return tempPath;
    } catch (error) {
      this.logger.error('Failed to download image', createErrorLogContext(error, { url }));
      throw error;
    }
  }

  /**
   * 获取图片扩展名
   */
  private getImageExtension(url: string, contentType: string | null): string {
    // 从URL获取扩展名
    const urlExt = url.split('.').pop()?.toLowerCase();
    if (urlExt && ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(urlExt)) {
      return urlExt;
    }

    // 从Content-Type获取扩展名
    if (contentType) {
      if (contentType.includes('jpeg')) return 'jpg';
      if (contentType.includes('png')) return 'png';
      if (contentType.includes('gif')) return 'gif';
      if (contentType.includes('webp')) return 'webp';
    }

    // 默认使用jpg
    return 'jpg';
  }

  /**
   * 保存PPT文件
   */
  private async savePpt(ppt: PptxGenJs, outputPath: string): Promise<void> {
    return new Promise((resolve, reject) => {
      ppt.writeFile({ fileName: outputPath })
        .then(() => {
          this.logger.debug('PPT file saved successfully', { outputPath });
          resolve();
        })
        .catch(error => {
          this.logger.error('Failed to save PPT file', createErrorLogContext(error, { outputPath }));
          reject(error);
        });
    });
  }
}

// 内部类型定义
interface ParsedSlide {
  title: string;
  level: number;
  content: ParsedContent[];
  images: ParsedImage[];
  lists: ParsedList[];
}

interface ParsedContent {
  type: 'paragraph' | 'heading';
  text: string;
  level?: number;
}

interface ParsedImage {
  src: string;
  alt: string;
  title: string;
}

interface ParsedList {
  type: 'bullet' | 'ordered';
  items: string[];
}
